Repository navigation
Backport #4216 to 4.x: make Array.prototype.concat spread host arrays and lists - #4219
Merged
Merged
Conversation
…script sees as arrays in Array.prototype.concat IsConcatSpreadable falls back to IsArray, which a host wrapper is not, so a live view over a CLR array (ArrayConversionMode.LiveView, the default on 4.x) or a List<T> was appended as a single element. A wrapper that exposes its target's elements by index is now spreadable. Adapted for 4.x: the fallback there is IsArray() rather than main's IsSpecArray(), and the test lives in Jint.Tests/Runtime with xUnit [Fact]s. Fixes sebastienros#4201. Adapted from 0a6288a Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 7, 2026
bgarcevic
pushed a commit
to bgarcevic/tomix-cli
that referenced
this pull request
Oct 12, 2026
Updated [Jint](https://github.com/sebastienros/jint) from 4.16.4 to 4.17.0. <details> <summary>Release notes</summary> _Sourced from [Jint's releases](https://github.com/sebastienros/jint/releases)._ ## 4.17.0 Jint 4.17.0 is a release from the `4.x` branch. It backports correctness and conformance fixes from `main` (four of them for scripts that could end the host process), linear string building, and **two new opt-in APIs for hosts**. That is why it is a minor release rather than a patch. Nothing in it changes an existing API or an existing default, so it is a drop-in update from 4.16.4. ### Highlights **A pooled engine can let go of a finished request's objects.** Hosts that reuse one engine across many evaluations (`CaptureGlobalSnapshot` once, `RestoreGlobalSnapshot` between runs) keep the interpreter's warmed caches, and that is the point of reusing it. But a warmed call site remembers the last function it called, and a warmed member read remembers the last object it read from. In a pooled web host, that can be a delegate built from a finished request's services, which then stay alive until the same script runs again. The new `Engine.Advanced.DiscardInterpreterCaches()` drops those caches, so the objects can be collected. The engine, its intrinsics and its global object stay as they are. The next run of each script rebuilds its cache, which costs roughly what a first run on a new engine costs, minus building the engine. A good place to call it is when an engine has been idle for a while, rather than after every use (#4229, from #4227). **A host can carry its own state across `await`.** Jint now runs ECMAScript's HostMakeJobCallback and HostCallJobCallback steps for every promise reaction, every thenable job and the `FinalizationRegistry` cleanup callback. A host can take part through the new `Options.Host.JobCallbacks`, which accepts a `JobCallbackHooks` with `Capture`, `Enter` and `Exit` callbacks. That lets each branch of a `Promise.all` fan-out keep its own host context, such as a logging scope or ambient request data, through every `await` (#4231, from #4230; issue #4200). The default is no hooks, in which case each promise registration costs one null check and nothing a script can observe changes. **Four more ways for a script to end the host process are closed.** - **ShadowRealm.** A failure crossing a ShadowRealm boundary threw its `TypeError` copy from inside the `catch` that was handling the original failure. That added a nested exception dispatch at every hop, and no stack check could see it. Even with the stack-overflow guard on, a long chain of wrapped functions ended the process on the way back out, and a three-line script with a `name` getter needed no chain at all. Building that copy also ran script (`toString`, getters, proxy traps); it no longer does. A failed `importValue` now reports the import failure instead of an unrelated error (#4220, from #4176 and #4183). - **`instanceof` and `eval`.** `instanceof` now checks the native stack before calling a custom `Symbol.hasInstance` method, and `eval` checks it before parsing. A self-referencing `Symbol.hasInstance`, or `var s = 'eval(s)'; eval(s)`, now raises a `RangeError` the script can catch instead of ending the process. On the `MaxExecutionStackCount` path, `eval` also stopped hopping to a new thread at each overflow without ever throwing. `instanceof` over a bound function now consults every bound link's own `Symbol.hasInstance`, as the specification requires (#4221, from #4172, #4184 and #4187). - **These checks follow 4.x's existing rule.** Like every other stack check on 4.x, they are active only when `Options.Constraints.StackOverflowGuard` is on, so turn it on for any script you do not fully trust. **Building a string with `s = s + x` is linear.** A `+` whose result reaches 512 characters now produces a deferred string instead of copying both sides. Building with `s = s + x`, `s = x + s` and `s = s + a + b` therefore becomes linear, as `s += x` already was. Short concatenations take a separate path and pay nothing for this. The deferred string is safe to read from several engines that share one `Prepared<Script>`, and it is charged to `LimitMemory` when it is built, so a memory limit still bounds it. Binding an already-bound function also no longer copies its whole `"bound …"` name at every level (#4160, from #3386, #3571, #4130, #4164 and #4173). **`Array.prototype.concat` spreads host arrays and lists.** Since 4.14, CLR arrays and lists reach script as live views by default. `concat` added such a view as a single element instead of spreading its items, so `a.concat(b)` returned the two wrappers. It now spreads anything script sees as an array (#4219, from #4216; issue #4201). **`TypedArray.prototype.with` survives a shrinking coercion.** When converting the index or the value made a resizable buffer shorter, `with` copied past its new end and let a CLR `ArgumentException` escape. It now copies only what the buffer still holds (#4218, from #4157). ### Verification Every backport was verified failing-first: its tests were run against the unfixed `4.x` tree on .NET 10 and .NET Framework 4.7.2, then with the change. The release candidate was measured against 4.16.4 on SunSpider and Dromaeo in six paired rounds, alternating which build ran first in each round. The four Dromaeo `ObjectRegExp` rows ran **12–15% faster** and SunSpider's `3d-raytrace` 2.3% faster. No row regressed by 1% or more; the two rows whose interval did not include zero (one Dromaeo `Cube` variant, +0.70%, and SunSpider's `controlflow-recursive`, +0.88%) are within this machine's run-to-run noise. ## What's Changed * Backport culture-independent overload-scoring fixture to 4.x by @PrzemyslawKlys in sebastienros/jint#4215 * Backport #4216 to 4.x: make Array.prototype.concat spread host arrays and lists by @lahma in sebastienros/jint#4219 * Backport #4176, #4183 to 4.x: keep a ShadowRealm failure copy from killing the host or running script by @lahma in sebastienros/jint#4220 * Backport #3386, #3571, #4130, #4164 and #4173 to 4.x: linear string building, safe across engines and charged to LimitMemory by @lahma in sebastienros/jint#4160 * Backport #4172, #4184, #4187 to 4.x: stop instanceof and eval recursions from killing the host by @lahma in sebastienros/jint#4221 * Backport #4157 to 4.x: Fix TypedArray.prototype.with after a shrinking coercion by @lahma in sebastienros/jint#4218 * Backport #4227 to 4.x: Let a reused engine discard the host objects its warmed caches hold by @lahma in sebastienros/jint#4229 * Backport #4230 to 4.x: Let a host carry its own state across await through HostMakeJobCallback by @lahma in sebastienros/jint#4231 **Full Changelog**: sebastienros/jint@v4.16.4...v4.17.0 Commits viewable in [compare view](sebastienros/jint@v4.16.4...v4.17.0). </details> Updated [Microsoft.AnalysisServices](https://docs.microsoft.com/en-us/sql/analysis-services/tabular-models/tabular-models-ssas?view=sql-server-2017) from 19.117.0 to 19.118.3. Updated [Microsoft.AnalysisServices.AdomdClient](https://docs.microsoft.com/en-us/sql/analysis-services/tabular-models/tabular-models-ssas?view=sql-server-2017) from 19.117.0 to 19.118.3. Updated [xunit.runner.visualstudio](https://github.com/xunit/visualstudio.xunit) from 4.0.0 to 4.0.1. <details> <summary>Release notes</summary> _Sourced from [xunit.runner.visualstudio's releases](https://github.com/xunit/visualstudio.xunit/releases)._ ## 4.0.1 Release notes: https://xunit.net/releases/visualstudio/4.0.1 Commits viewable in [compare view](xunit/visualstudio.xunit@4.0.0...4.0.1). </details> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Oct 12, 2026
PatrickSt1991
pushed a commit
to Apps2Samsung/Apps2Samsung
that referenced
this pull request
Oct 13, 2026
Updated [Jint](https://github.com/sebastienros/jint) from 4.16.4 to 4.17.0. <details> <summary>Release notes</summary> _Sourced from [Jint's releases](https://github.com/sebastienros/jint/releases)._ ## 4.17.0 Jint 4.17.0 is a release from the `4.x` branch. It backports correctness and conformance fixes from `main` (four of them for scripts that could end the host process), linear string building, and **two new opt-in APIs for hosts**. That is why it is a minor release rather than a patch. Nothing in it changes an existing API or an existing default, so it is a drop-in update from 4.16.4. ### Highlights **A pooled engine can let go of a finished request's objects.** Hosts that reuse one engine across many evaluations (`CaptureGlobalSnapshot` once, `RestoreGlobalSnapshot` between runs) keep the interpreter's warmed caches, and that is the point of reusing it. But a warmed call site remembers the last function it called, and a warmed member read remembers the last object it read from. In a pooled web host, that can be a delegate built from a finished request's services, which then stay alive until the same script runs again. The new `Engine.Advanced.DiscardInterpreterCaches()` drops those caches, so the objects can be collected. The engine, its intrinsics and its global object stay as they are. The next run of each script rebuilds its cache, which costs roughly what a first run on a new engine costs, minus building the engine. A good place to call it is when an engine has been idle for a while, rather than after every use (#4229, from #4227). **A host can carry its own state across `await`.** Jint now runs ECMAScript's HostMakeJobCallback and HostCallJobCallback steps for every promise reaction, every thenable job and the `FinalizationRegistry` cleanup callback. A host can take part through the new `Options.Host.JobCallbacks`, which accepts a `JobCallbackHooks` with `Capture`, `Enter` and `Exit` callbacks. That lets each branch of a `Promise.all` fan-out keep its own host context, such as a logging scope or ambient request data, through every `await` (#4231, from #4230; issue #4200). The default is no hooks, in which case each promise registration costs one null check and nothing a script can observe changes. **Four more ways for a script to end the host process are closed.** - **ShadowRealm.** A failure crossing a ShadowRealm boundary threw its `TypeError` copy from inside the `catch` that was handling the original failure. That added a nested exception dispatch at every hop, and no stack check could see it. Even with the stack-overflow guard on, a long chain of wrapped functions ended the process on the way back out, and a three-line script with a `name` getter needed no chain at all. Building that copy also ran script (`toString`, getters, proxy traps); it no longer does. A failed `importValue` now reports the import failure instead of an unrelated error (#4220, from #4176 and #4183). - **`instanceof` and `eval`.** `instanceof` now checks the native stack before calling a custom `Symbol.hasInstance` method, and `eval` checks it before parsing. A self-referencing `Symbol.hasInstance`, or `var s = 'eval(s)'; eval(s)`, now raises a `RangeError` the script can catch instead of ending the process. On the `MaxExecutionStackCount` path, `eval` also stopped hopping to a new thread at each overflow without ever throwing. `instanceof` over a bound function now consults every bound link's own `Symbol.hasInstance`, as the specification requires (#4221, from #4172, #4184 and #4187). - **These checks follow 4.x's existing rule.** Like every other stack check on 4.x, they are active only when `Options.Constraints.StackOverflowGuard` is on, so turn it on for any script you do not fully trust. **Building a string with `s = s + x` is linear.** A `+` whose result reaches 512 characters now produces a deferred string instead of copying both sides. Building with `s = s + x`, `s = x + s` and `s = s + a + b` therefore becomes linear, as `s += x` already was. Short concatenations take a separate path and pay nothing for this. The deferred string is safe to read from several engines that share one `Prepared<Script>`, and it is charged to `LimitMemory` when it is built, so a memory limit still bounds it. Binding an already-bound function also no longer copies its whole `"bound …"` name at every level (#4160, from #3386, #3571, #4130, #4164 and #4173). **`Array.prototype.concat` spreads host arrays and lists.** Since 4.14, CLR arrays and lists reach script as live views by default. `concat` added such a view as a single element instead of spreading its items, so `a.concat(b)` returned the two wrappers. It now spreads anything script sees as an array (#4219, from #4216; issue #4201). **`TypedArray.prototype.with` survives a shrinking coercion.** When converting the index or the value made a resizable buffer shorter, `with` copied past its new end and let a CLR `ArgumentException` escape. It now copies only what the buffer still holds (#4218, from #4157). ### Verification Every backport was verified failing-first: its tests were run against the unfixed `4.x` tree on .NET 10 and .NET Framework 4.7.2, then with the change. The release candidate was measured against 4.16.4 on SunSpider and Dromaeo in six paired rounds, alternating which build ran first in each round. The four Dromaeo `ObjectRegExp` rows ran **12–15% faster** and SunSpider's `3d-raytrace` 2.3% faster. No row regressed by 1% or more; the two rows whose interval did not include zero (one Dromaeo `Cube` variant, +0.70%, and SunSpider's `controlflow-recursive`, +0.88%) are within this machine's run-to-run noise. ## What's Changed * Backport culture-independent overload-scoring fixture to 4.x by @PrzemyslawKlys in sebastienros/jint#4215 * Backport #4216 to 4.x: make Array.prototype.concat spread host arrays and lists by @lahma in sebastienros/jint#4219 * Backport #4176, #4183 to 4.x: keep a ShadowRealm failure copy from killing the host or running script by @lahma in sebastienros/jint#4220 * Backport #3386, #3571, #4130, #4164 and #4173 to 4.x: linear string building, safe across engines and charged to LimitMemory by @lahma in sebastienros/jint#4160 * Backport #4172, #4184, #4187 to 4.x: stop instanceof and eval recursions from killing the host by @lahma in sebastienros/jint#4221 * Backport #4157 to 4.x: Fix TypedArray.prototype.with after a shrinking coercion by @lahma in sebastienros/jint#4218 * Backport #4227 to 4.x: Let a reused engine discard the host objects its warmed caches hold by @lahma in sebastienros/jint#4229 * Backport #4230 to 4.x: Let a host carry its own state across await through HostMakeJobCallback by @lahma in sebastienros/jint#4231 **Full Changelog**: sebastienros/jint@v4.16.4...v4.17.0 Commits viewable in [compare view](sebastienros/jint@v4.16.4...v4.17.0). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This was referenced Oct 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This backports one fix from main to 4.x. A second candidate was skipped because 4.x does not have the code it changes.
Array.prototype.concatnested host arrays and lists instead of spreading them (#4201). Since 4.14,ArrayConversionMode.LiveViewhas been the default, and under itengine.SetValue("a", new[] { x }); a.concat(b)returned a two-element array holding the two wrappers. The same happened with anyList<T>.Adapted for 4.x
IsConcatSpreadablefalls back toIsArray(), not main'sIsSpecArray(). The fix keeps that call and adds the same wrapper test after it:IsArray() || this is ObjectWrapper { IsArrayLike: true, HasIndexedElements: true }. On 4.x,ObjectWrapper.IsArrayLikeandHasIndexedElementsmatch main, including theTypeResolvermember-filter gate inHasIndexedElements. A queue, a stack or a set still is not spread. AJsArraystill never reaches the type test.Jint.Tests/Runtime/ClrCollectionConcatTests.cs(4.x has noRuntime/Interopfolder), and its[Test]attributes became xUnit[Fact]s. 4.x has noSpecAnchors.txt, so main's anchor-list change is not included.Evidence
The seven tests in
ClrCollectionConcatTestswere run first without the fix.ConcatSpreadsTwoClrArraysOfExpandoObjectsis the host code from #4201, as written in the issue.The test that passes without the fix is
ConcatDoesNotSpreadACollectionWithoutIndexedElements. It checks that aQueue<T>stays a single element, so it should pass both before and after the fix.Skipped
git grep MaxOutputCharacters upstream/4.xandgit grep ConvertResult upstream/4.xboth find nothing, and there is noResultConverter.MaxOutputCharacterswas added on main by Bound result conversion, JSON serialization, and error rendering #3046, which is part of the Bound parser source length and AST size #3037-Add hardened untrusted-code profile #3060 security stack that 4.x does not have. Without that limit there is nothing to check earlier.Test totals (Release, after the fix)
The public API Verify snapshots are unchanged; the working tree was clean after the runs.
🤖 Generated with Claude Code